#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "config.pb.h"
#include "dump_hex.h"
#include "pb.h"
#include "pb_decode.h"
#include "pb_encode.h"

typedef struct
{
    uint8_t *p;         // 缓冲区指针
    uint32_t capacity;  // 缓冲区长度
    uint32_t length;    // 数据长度
} pb_buffer_info_t;     /* protobuf编解码回调参数类型 */

typedef enum
{
    LOG_LEVEL_DEBUG   = 0,
    LOG_LEVEL_INFO    = 1,
    LOG_LEVEL_WARNING = 2,
    LOG_LEVEL_ERROR   = 3
} log_level_t;

typedef struct
{
    char        terminal_sn[20];  // 终端序列号
    log_level_t log_level;        // 日志级别
} device_info_t;

typedef struct
{
    char     hostname[40];  // 主机名
    uint16_t port;          // 端口号
} server_info_t;

typedef struct
{
    bool     use_dhcp;     // 是否使用dhcp
    uint32_t ip;           // ip地址
    uint32_t gateway;      // 网关地址
    uint32_t subnet_mask;  // 子网掩码
    uint32_t dns;          // dns地址
    char     mac_addr[6];  // mac地址
} net_info_t;

typedef struct
{
    device_info_t device_info;  // 设备信息
    net_info_t    net_info;     // 网络信息
    server_info_t server_info;  // 服务器信息
} terminal_config_t;

/* IP地址的格式化字符串 */
#define IP_FMT "%d.%d.%d.%d"
/* uint32转IP */
#define INT_TO_IP(ip) (uint8_t)((ip >> 24) & 0xFF), (uint8_t)((ip >> 16) & 0xFF), (uint8_t)((ip >> 8) & 0xFF), (uint8_t)(ip & 0xFF)
/* IP转uint32 */
#define IP_TO_INT(ip1, ip2, ip3, ip4) (uint32_t)((ip1 << 24) | (ip2 << 16) | (ip3 << 8) | ip4)

static void config_t_encode(terminal_config_t *config, uint8_t *temp_buf, uint32_t temp_buf_size, uint32_t *data_length);
static void config_t_decode(terminal_config_t *config, uint8_t *temp_buf, uint32_t temp_buf_size);
static void config_t_init(terminal_config_t *config);
static void config_t_print_out(const terminal_config_t *config);
static bool config_t_equal(const terminal_config_t *a, const terminal_config_t *b);

int main()
{
    static uint8_t    buffer[128];
    terminal_config_t terminal_config = {0};
    uint32_t          data_len        = 0;

    printf("****************************************\r\n");
    printf("原始数据：\r\n");
    config_t_init(&terminal_config);
    config_t_print_out(&terminal_config);

    printf("****************************************\r\n");
    printf("序列化：\r\n");
    config_t_encode(&terminal_config, buffer, sizeof(buffer), &data_len);
    dump_hex(buffer, 64);

    printf("****************************************\r\n");
    printf("反序列化：\r\n");
    terminal_config_t config2 = {0};
    config_t_decode(&config2, buffer, data_len);
    config_t_print_out(&terminal_config);

    printf("****************************************\r\n");
    printf("对象比较：\r\n");
    // 比较两个对象
    if (config_t_equal(&terminal_config, &config2))
    {
        // 复制前后对象相等
        printf("对象相等\r\n");
    }
    else
    {
        printf("！！！！！对象不相等\r\n");
    }

    return 0;
}

/* 编码变长数据 */
bool encode_buffer(pb_ostream_t *stream, const pb_field_t *field, void *const *arg)
{
    const pb_buffer_info_t *buf_info = (const pb_buffer_info_t *)*arg;
    if (!pb_encode_tag_for_field(stream, field))
    {
        return false;
    }
    return pb_encode_string(stream, buf_info->p, buf_info->length);
}

/* 解码变长数据 */
bool decode_buffer(pb_istream_t *stream, const pb_field_t *field, void **arg)
{
    pb_buffer_info_t *buf_info = (pb_buffer_info_t *)*arg;
    buf_info->length           = stream->bytes_left;
    /* 判断是否超出缓冲区容量 */
    if (buf_info->length > buf_info->capacity)
    {
        return false;
    }
    if (!pb_read(stream, buf_info->p, buf_info->length))
    {
        return false;
    }
    return true;
}

void config_t_init(terminal_config_t *config)
{
    // 构造演示结构体的数据
    memset(config, 0, sizeof(terminal_config_t));
    strcpy(config->device_info.terminal_sn, "123456");
    config->device_info.log_level = LOG_LEVEL_INFO;

    memcpy(config->net_info.mac_addr, (uint8_t[6]){0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 6);
    config->net_info.dns         = IP_TO_INT(8, 8, 8, 8);
    config->net_info.gateway     = IP_TO_INT(192, 168, 1, 1);
    config->net_info.ip          = IP_TO_INT(192, 168, 1, 100);
    config->net_info.subnet_mask = IP_TO_INT(255, 255, 255, 0);
    config->net_info.use_dhcp    = true;

    strcpy(config->server_info.hostname, "dengyongsheng.cn");
    config->server_info.port = 8080;
}

void config_t_encode(terminal_config_t *config, uint8_t *temp_buf, uint32_t temp_buf_size, uint32_t *data_length)
{
    PB_TerninalConfigration pb_ter_config = PB_TerninalConfigration_init_zero;

    /* 保存sn号 */
    pb_buffer_info_t sn_info                  = {.length = strlen(config->device_info.terminal_sn), .p = (uint8_t *)config->device_info.terminal_sn};
    pb_ter_config.device_info.terminal_sn.arg = &sn_info;
    pb_ter_config.device_info.terminal_sn.funcs.encode = encode_buffer;

    pb_ter_config.device_info.log_level = config->device_info.log_level;

    pb_ter_config.has_device_info = true;

    /* 保存网络配置 */
    pb_ter_config.net_info.use_dhcp = config->net_info.use_dhcp;

    /* 保存mac地址 */
    pb_buffer_info_t mac_info                    = {.length = sizeof(config->net_info.mac_addr), .p = (uint8_t *)config->net_info.mac_addr};
    pb_ter_config.net_info.mac_addr.funcs.encode = encode_buffer;
    pb_ter_config.net_info.mac_addr.arg          = &mac_info;

    pb_ter_config.net_info.ip          = config->net_info.ip;           // 设备配置的ip地址
    pb_ter_config.net_info.dns         = config->net_info.dns;          // 设备配置的dns地址
    pb_ter_config.net_info.gateway     = config->net_info.gateway;      // 设备配置的网关地址
    pb_ter_config.net_info.subnet_mask = config->net_info.subnet_mask;  // 设备子网掩码
    pb_ter_config.has_net_info         = true;

    /* 保存服务器配置 */
    pb_buffer_info_t hostname_info                  = {.length = strlen(config->server_info.hostname), .p = (uint8_t *)config->server_info.hostname};
    pb_ter_config.server_info.hostname.arg          = &hostname_info;
    pb_ter_config.server_info.hostname.funcs.encode = encode_buffer;

    pb_ter_config.server_info.port = config->server_info.port;  // 服务器端口号

    pb_ter_config.has_server_info = true;

    pb_ostream_t out = pb_ostream_from_buffer(temp_buf, temp_buf_size);
    /* 转换为字节数组 */
    if (!pb_encode(&out, PB_TerninalConfigration_fields, &pb_ter_config))
    {
        printf("!!!!!encode config error\r\n");
        return;
    }
    // 输出字节数量
    *data_length = out.bytes_written;
}

void config_t_decode(terminal_config_t *config, uint8_t *temp_buf, uint32_t temp_buf_size)
{
    PB_TerninalConfigration pb_ter_config = PB_TerninalConfigration_init_zero;

    /* 解析sn号配置 */
    pb_buffer_info_t sn_info = {.p = (uint8_t *)config->device_info.terminal_sn, .capacity = sizeof(config->device_info.terminal_sn)};
    pb_ter_config.device_info.terminal_sn.arg          = &sn_info;
    pb_ter_config.device_info.terminal_sn.funcs.decode = decode_buffer;

    /* 解析mac地址配置 */
    pb_buffer_info_t mac_info                    = {.p = (uint8_t *)config->net_info.mac_addr, .capacity = sizeof(config->net_info.mac_addr)};
    pb_ter_config.net_info.mac_addr.arg          = &mac_info;
    pb_ter_config.net_info.mac_addr.funcs.decode = decode_buffer;

    /* 解析服务器主机名配置 */
    pb_buffer_info_t server_hostname_info  = {.p = (uint8_t *)config->server_info.hostname, .capacity = sizeof(config->server_info.hostname)};
    pb_ter_config.server_info.hostname.arg = &server_hostname_info;
    pb_ter_config.server_info.hostname.funcs.decode = decode_buffer;

    /* 执行protobuf解析 */
    pb_istream_t in = pb_istream_from_buffer((uint8_t *)temp_buf, temp_buf_size);

    if (!pb_decode(&in, PB_TerninalConfigration_fields, &pb_ter_config))
    {
        // protobuf解析错误
        printf("!!!!protobuf decode config error\r\n");
        return;
    }
    config->device_info.terminal_sn[sn_info.length]           = '\0';
    config->device_info.log_level                             = pb_ter_config.device_info.log_level;
    config->net_info.dns                                      = pb_ter_config.net_info.dns;
    config->net_info.ip                                       = pb_ter_config.net_info.ip;
    config->net_info.use_dhcp                                 = pb_ter_config.net_info.use_dhcp;
    config->net_info.subnet_mask                              = pb_ter_config.net_info.subnet_mask;
    config->net_info.gateway                                  = pb_ter_config.net_info.gateway;
    config->server_info.hostname[server_hostname_info.length] = '\0';
    config->server_info.port                                  = pb_ter_config.server_info.port;
}

void config_t_print_out(const terminal_config_t *config)
{
    // 打印config结构体的内容
    printf("终端序列号：%s\r\n", config->device_info.terminal_sn);
    printf("日志级别：%d\r\n", config->device_info.log_level);
    printf("主机名：%s\r\n", config->server_info.hostname);
    printf("端口号：%d\r\n", config->server_info.port);
    printf("是否使用dhcp：%s\r\n", config->net_info.use_dhcp ? "是" : "否");
    printf("ip地址：" IP_FMT "\r\n", INT_TO_IP(config->net_info.ip));
    printf("网关地址：" IP_FMT "\r\n", INT_TO_IP(config->net_info.gateway));
    printf("子网掩码：" IP_FMT "\r\n", INT_TO_IP(config->net_info.subnet_mask));
    printf("dns地址：" IP_FMT "\r\n", INT_TO_IP(config->net_info.dns));
    printf("mac地址：");
    for (int i = 0; i < sizeof(config->net_info.mac_addr); i++)
    {
        printf("%02x ", config->net_info.mac_addr[i]);
    }
    printf("\r\n");
}

/* 比较对象 */
bool config_t_equal(const terminal_config_t *a, const terminal_config_t *b)
{
    // 比较每一个字段
    if (a->device_info.log_level != b->device_info.log_level)
    {
        printf("device_info.log_level not equal(%d<>%d)!\r\n", a->device_info.log_level, b->device_info.log_level);
        return false;
    }
    if (strcmp(a->device_info.terminal_sn, b->device_info.terminal_sn) != 0)
    {
        printf("device_info.terminal_sn not equal!\r\n");
        return false;
    }
    if (a->net_info.use_dhcp != b->net_info.use_dhcp)
    {
        printf("net_info.use_dhcp not equal!\r\n");
        return false;
    }
    if (a->net_info.ip != b->net_info.ip)
    {
        printf("net_info.ip not equal!\r\n");
        return false;
    }
    if (a->net_info.gateway != b->net_info.gateway)
    {
        printf("net_info.gateway not equal!\r\n");
        return false;
    }
    if (a->net_info.subnet_mask != b->net_info.subnet_mask)
    {
        printf("net_info.subnet_mask not equal!\r\n");
        return false;
    }
    if (memcmp(a->net_info.mac_addr, b->net_info.mac_addr, sizeof(a->net_info.mac_addr)) != 0)
    {
        printf("net_info.mac_addr not equal!\r\n");
        return false;
    }
    if (strcmp(a->server_info.hostname, b->server_info.hostname) != 0)
    {
        printf("server_info.hostname not equal!\r\n");
        return false;
    }
    if (a->server_info.port != b->server_info.port)
    {
        printf("server_info.port not equal!\r\n");
        return false;
    }
    return true;
}
